package com.example.auth.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.auth.domain.DataNode;
import com.example.auth.domain.User;
import com.example.auth.mapper.UserMapper;
import com.example.auth.service.DataNodeService;
import com.example.auth.service.UserService;
import com.example.config.exception.GlobalExcetion;
import com.example.util.StringSplitUtil;
import com.example.util.TotalBasedRoundRobinAllocator;
import com.example.util.sql.*;
import com.example.util.tableshard.TableShardingAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
* @author CETC
* @description 针对表【user】的数据库操作Service实现
* @createDate 2025-09-21 15:13:06
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
    implements UserService{

    // 创建固定大小的线程池
    private static final ExecutorService executor = Executors.newFixedThreadPool(5);

    /**
     * 表名前缀
     */
    private final static String USER_TABLE_SUFFIX = "sys_user_";

    /**
     * 用户表数量
     */
    private final static Integer USER_TABLE_COUNT=4;

    /**
     * 用户id前缀
     */
    private final static String USER_ID_SUFFIX="sys_";

    /**
     * 用户默认节点
     */
    private final static String USER_DEFAULT_NODE="data-server";

    @Autowired
    private DataSource dataSource;

    @Autowired
    private DataNodeService dataNodeService;

    @Autowired
    @Lazy
    private PasswordEncoder passwordEncoder;

    @Override
    public User findByUsername(String username) {
        String tableName=this.getUserTableByUserName(username);
        return this.baseMapper.getUserByUsername(tableName,username);
    }

    @Override
    public User registerUser(User user) throws GlobalExcetion {
        return this.registerUserByHashShard(user);
    }

    @Override
    public Integer findUserTotalNum() throws ExecutionException, InterruptedException {
       return this.findUserHashTotalNum();
    }

    @Override
    public Integer findUserTotalByTable(String tableName) {
        return this.baseMapper.getUserCountByTable(tableName);
    }

    @Override
    public Integer findUserHashTotalNum() throws ExecutionException, InterruptedException {
        List<String> tableAlls= TableUtils.getTables(USER_TABLE_SUFFIX,USER_TABLE_COUNT);
        List<CompletableFuture<Integer>> futures = new ArrayList<>();
        for (int i = 0; i < tableAlls.size() ; i++) {
            String tableName=tableAlls.get(i);
            DBSql dbSql=new DBSql(dataSource);
            CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
                try {
                    String sql=String.format("select count(1) from %s where is_deleted='0'",tableName);
                    System.out.println("执行sql: "+sql);
                    return dbSql.getInteger(sql);
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }, executor);
            futures.add(future);
        }
        // 等待所有任务完成并收集结果
        CompletableFuture<Void> allFutures = CompletableFuture.allOf(
                futures.toArray(new CompletableFuture[0])
        );

        CompletableFuture<List<Integer>> allResults = allFutures.thenApply(v ->
                futures.stream()
                        .map(CompletableFuture::join).collect(Collectors.toList())
        );
        // 获取最终结果
        List<Integer> results = allResults.get();
        return results.stream().mapToInt(Integer::intValue).sum();
    }

    @Override
    public User updateUserById(User user) throws GlobalExcetion {
        String userTable=this.getUserTableByUserName(user.getUsername());
        List<String> strings=new ArrayList<>();
        strings.add("uuid");
        SqlResult sqlResult=SqlGenerator.generateUpdate(userTable,user,strings);
        DBSql dbSql=new DBSql(dataSource);
        dbSql.executeUpdate(sqlResult.getSql(),sqlResult.getParams().toArray());
        return user;
    }


    @Override
    public void updatePwd(String username, String password) {
        String userTableName=this.getUserTableByUserName(username);
        User user = this.baseMapper.getUserByUsername(userTableName,username);
        if(user != null){
            user.setPassword(passwordEncoder.encode(password));
            user.setUpdateTime(new Date());
            List<String> fields=new ArrayList<>();
            fields.add("uuid");
            SqlResult sqlResult=SqlGenerator.generateUpdate(userTableName,user,fields);
            DBSql dbSql=new DBSql(dataSource);
            System.out.println("执行sql: "+sqlResult.getSql()+"");
            dbSql.executeUpdate(sqlResult.getSql(),sqlResult.getParams().toArray());
        }
    }

    @Override
    public DataNode findDataNodeByUserName(String userName) {
        String tableName=this.getUserTableByUserName(userName);
        User user = this.baseMapper.getUserByUsername(tableName,userName);
        if(user == null || user.getNodeId() == null){
            return null;
        }
        return this.findDataNodeByUser(user);
    }

    @Override
    public DataNode findDataNodeByUserId(String userId) {
        String tableName=this.getUserTableByUserId(userId);
        User user=this.baseMapper.getUserByUserId(tableName,userId);
        return this.findDataNodeByUser(user);
    }

    @Override
    public DataNode findDataNodeByUser(User user) {
        if(user == null || user.getNodeId() == null){
            return null;
        }
        List<String> strings=StringSplitUtil.splitToList(user.getNodeId());
        List<DataNode> dataNodes=dataNodeService.getDataNodeListByNodeNo(USER_DEFAULT_NODE);
        return this.getDataNodeFirst(strings,dataNodes);
    }

    @Override
    public DataNode findDataNodeByUser(User user, String nodeNo) throws GlobalExcetion {
        if(user == null || user.getNodeId() == null){
            return null;
        }
        List<String> strings=StringSplitUtil.splitToList(user.getNodeId());
        List<DataNode> dataNodes=dataNodeService.getDataNodeListByNodeNo(nodeNo);
        if(dataNodes==null || dataNodes.isEmpty()){
            throw new GlobalExcetion(1,"节点编号错误");
        }
        return this.getDataNodeFirst(strings,dataNodes);
    }

    /**
     * 节点获取，默认获取第一个
     * @param nodeIds 用户节点列表
     * @param dataNodes  数据节点列表
     * @return
     */
    private DataNode getDataNodeFirst(List<String> nodeIds,List<DataNode> dataNodes){
        for(DataNode dataNode:dataNodes){
            for(String nodeId:nodeIds){
                if(nodeId.equals(dataNode.getId())){
                    return dataNode;
                }
            }
        }
        return null;
    }

    @Override
    public User getUserByHashUserId(String userId) {
        String tableName=this.getUserTableByUserId(userId);
        return this.baseMapper.getUserByUserId(tableName,userId);
    }

    @Override
    public User getUserByHashUserName(String userName) {
        String tableName=this.getUserTableByUserName(userName);
        return this.baseMapper.getUserByUsername(tableName,userName);
    }

    @Override
    public User registerUserByHashShard(User user) throws GlobalExcetion {
        String userTableName=this.getUserTableByUserName(user.getUsername());
        if(userTableName == null){
            throw new GlobalExcetion(1,"系统错误，请稍后再试");
        }
        System.out.println("用户table: "+userTableName);
        User oldUser = this.baseMapper.getUserByUsername(userTableName, user.getUsername());
        if(oldUser != null ){
            throw new GlobalExcetion(101,"用户已存在");
        }
        if(user.getSex()==null){
            user.setSex(1);
        }
        String userId=this.createUserIdByUserTable(user.getUsername());
        user.setUuid(userId);
        user.setSystemUser(0);
        user.setIsDeleted(0);
        user.setCreateTime(new Date());
        user.setUpdateTime(new Date());
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        Integer total=this.findUserTotalByTable(userTableName);
        List<DataNode> list=dataNodeService.getDataNodeListByNodeNo(USER_DEFAULT_NODE);
        TotalBasedRoundRobinAllocator<DataNode> base=new TotalBasedRoundRobinAllocator(list);
        DataNode nodeConfig=base.allocateNodeByTotal(total);
        user.setNodeId(nodeConfig.getId());
        DBSql dbSql=new DBSql(dataSource);
        SqlResult sqlResult=SqlGenerator.generateInsert(userTableName,user);
        dbSql.executeUpdate(sqlResult.getSql(),sqlResult.getParams().toArray());
        return user;
    }

    @Override
    public String getUserTableByUserName(String userName) {
        String index=getUserTableIndexByUserName(userName);
        return USER_TABLE_SUFFIX+index;
    }

    @Override
    public String getUserTableByUserId(String userId) {
        String[] strings=userId.split("_");
        if(strings.length < 2){
            return "";
        }
        return USER_TABLE_SUFFIX+strings[1];
    }

    @Override
    public String createUserIdByUserTable(String userName) {
        String id= IdUtils.getUUID();
        String index=getUserTableIndexByUserName(userName);
        return USER_ID_SUFFIX+index+"_"+id;
    }

    @Override
    public String getUserTableIndexByUserName(String userName) {
        if(userName==null || userName.isEmpty()){
            return "";
        }
        Integer tableIndex=TableShardingAlgorithm.getTableIndexByConsistentHash(userName,USER_TABLE_COUNT);
        return tableIndex.toString();
    }

    @Override
    public IPage<User> pageList(Page<User> page,String userName) {
        List<String> tables=TableUtils.getTables(USER_TABLE_SUFFIX,USER_TABLE_COUNT);
        String tableName=TableUtils.getUnionAllTable(tables);
        return this.baseMapper.selectUserByPage(page,tableName,userName);
    }

    @Override
    public Integer deleteUserByUserId(String userId) {
        String userTableName=this.getUserTableByUserId(userId);
        return this.baseMapper.deleteData(userTableName,userId);
    }

    @Override
    public DataNode findDataNodeByUserName(String userName, String nodeNo) throws GlobalExcetion {
        List<DataNode> list=dataNodeService.getDataNodeListByNodeNo(nodeNo);
        if(list==null){
            return null;
        }
        User user=this.getUserByHashUserName(userName);
        DataNode dataNode=this.findDataNodeByUser(user,nodeNo);
        if(dataNode==null){
            TotalBasedRoundRobinAllocator<DataNode> base=new TotalBasedRoundRobinAllocator(list);
            DataNode node=base.allocateNodeByHash(userName);
            List<String> nodeIds=StringSplitUtil.splitToList(user.getNodeId());
            if(!nodeIds.contains(node.getId())){
                String nodeIdStr=user.getNodeId()+","+node.getId();
                user.setNodeId(nodeIdStr);
                this.updateUserById(user);
            }
            return node;
        }
        return dataNode;
    }

    @Override
    public DataNode findDataNodeByUserId(String userId, String nodeNo) throws GlobalExcetion {
        List<DataNode> list=dataNodeService.getDataNodeListByNodeNo(nodeNo);
        if(list==null){
            return null;
        }
        User user=this.getUserByHashUserId(userId);
        DataNode dataNode=this.findDataNodeByUser(user,nodeNo);
        if(dataNode==null){
            TotalBasedRoundRobinAllocator<DataNode> base=new TotalBasedRoundRobinAllocator(list);
            DataNode node=base.allocateNodeByHash(user.getUsername());
            List<String> nodeIds=StringSplitUtil.splitToList(user.getNodeId());
            if(!nodeIds.contains(node.getId())){
                String nodeIdStr=user.getNodeId()+","+node.getId();
                user.setNodeId(nodeIdStr);
                this.updateUserById(user);
            }
            return node;
        }
        return dataNode;
    }


}




